<?php


namespace App\Service\Admin;


use _HumbugBox5f943a942674\Nette\Neon\Exception;
use App\Constants\ContextKey;
use App\Constants\ErrorCode;
use App\Dao\AdminMenuDao;
use App\Dao\AdminRoleDao;
use App\Dao\AdminUserDao;
use App\Exception\BusinessException;
use App\Model\AdminRole;
use App\Model\AdminUser;
use App\Service\Cache\AdminRoleCache;
use App\Utils\Captcha;
use App\Utils\Casbin;
use App\Utils\Jwt;
use App\Utils\Log;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Context;
use phpDocumentor\Reflection\Types\Mixed_;

class AdminUserService extends BaseService
{
    /**
     * @Inject()
     * @var AdminUserDao
     */
    protected $adminUserDao;

    /**
     * @Inject()
     * @var AdminMenuDao
     */
    protected $adminMenuDao;

    /**
     * @Inject()
     * @var AdminRoleDao
     */
    protected $adminRoleDao;

    protected $casbinInstance;

    public function __construct()
    {
        $this->casbinInstance = make(Casbin::class, [config('casbin.admin')]);
    }

    /**
     * @param array $where
     * @return array
     */
    public function index(array $where): array
    {
        if ($where['role_id']) {
            $userIds = $this->casbinInstance->getUsersByRole($where['role_id']);
            $where['ids'] = empty($userIds) ? [''] : $userIds;
        }
        $where['not_show_default'] = 1;
        $pageInfo = $this->extractPaginateInfo($where);
        $index = $this
            ->adminUserDao
            ->where($where)
            ->paginate($pageInfo['perPage']);
        return $this->formatIndex($index);
    }

    public function formatIndex(\Hyperf\Contract\LengthAwarePaginatorInterface $index): array
    {

        $adminRoleCache = di(AdminRoleCache::class);
        $data = [];
        /** @var AdminUSer $adminUser */
        foreach ($index->items() as $adminUser) {
            $roleId = $this->casbinInstance->getRolesByUser($adminUser->id)[0];
            $data[] = [
                'id' => $adminUser->id,
                'mobile' => $adminUser->mobile,
                'username' => $adminUser->username,
                'status' => $adminUser->status,
                'position' => $adminUser->position,
                'role_id' => $roleId,
                'role_name' => $adminRoleCache->get($roleId)['name'],
                'created_at' => $adminUser->created_at->toDateTimeString(),
            ];
        }

        return [
            'data' => $data,
            'current_page' => $index->currentPage(),
            'total' => $index->total(),
            'per_page' => $index->perPage()
        ];
    }

    /**
     * @param $id
     * @param array $data
     * @return AdminUser
     */
    public function save($id, array $data): AdminUser
    {
        if ($id) {
            /** @var AdminUser $user */
            $user = $this->adminUserDao->firstOrThrow(['id' => $id]);
            if ($user->mobile == config('menus.default_admin_user.mobile')) {
                throw new BusinessException(ErrorCode::ADMIN_DEFAULT_USER_CANT_CHANGE);
            }
        } else {
            $user = new AdminUser();
        }

        $user->password = password_hash($data['password'], PASSWORD_BCRYPT);
        $user->fill($data);

        $role = $this->adminRoleDao->firstOrThrow(['id' => $data['role_id']], ErrorCode::ADMIN_ROLE_NOT_FOUND);

        Db::transaction(
            function () use ($user, $data) {
                $user->save();
                $this->casbinInstance->setRolesForUser($user->id, [$data['role_id']]);
            }
        );
        return $user;
    }

    public function delete(int $id): void
    {
        Db::beginTransaction();
        try {
            /** @var AdminUser $user */
            dump($id);
            $user = $this->adminUserDao->firstOrThrow(['id' => $id]);
            dump($user->toArray());
            if ($user->mobile == config('menus.default_admin_user.mobile')) {
                throw new BusinessException(ErrorCode::ADMIN_DEFAULT_USER_CANT_CHANGE);
            }
            $user->delete();
            $this->casbinInstance->deleteUser($id);
            Db::commit();
        } catch (\Exception $exception) {
            Db::rollBack();
            Log::get()->error($exception->getMessage(), [
                'id' => $id,
                'user' => $user->toArray()
            ]);
            throw $exception;
        }
    }

    /**
     * @param string $mobile
     * @param string $password
     * @return array
     */
    public function login(string $mobile, string $password, string $captchaKey, string $captchaCode): array
    {
        $captcha = di(Captcha::class);

        $captcha->checkCaptcha($captchaKey, $captchaCode);

        $adminUser = $this->adminUserDao->first(['mobile' => $mobile]);

        //用户不存在
        if (null == $adminUser) {
            throw new BusinessException(ErrorCode::ADMIN_USER_NOT_FOUND);
        }

        //密码错误
        if (!password_verify($password, $adminUser->password)) {
            throw new BusinessException(ErrorCode::ADMIN_USER_PASSWORD_FAILED);
        }

        Context::set(ContextKey::ADMIN_USER, $adminUser);

        $token = Jwt::token(['id' => $adminUser->id]);

        //TODO 需要根据当前角色返回菜单
        $roleId = $this->casbinInstance->getRolesByUser($adminUser->id)[0];
        /** @var AdminRole $role */
        try {
            $role = $this->adminRoleDao->firstOrThrow(['id' => $roleId]);
        } catch (\Exception $exception) {
            Log::get()->error($exception->getMessage(), [
                'admin_user' => $adminUser->toArray(),
                'role_id' => $roleId
            ]);
            throw $exception;
        }

        $menus = $role->adminMenus;
        $tree = $this->adminMenuDao->tree($menus);

        return [
            'user' => $adminUser->toArray(),
            'token' => $token,
            'menus' => $tree,

        ];
    }


}